/*-------------------------------------------------------------------------
 *
 * wchar.c
 *	  用于处理各种编码中的多字节字符的函数。
 *
 * Portions Copyright (c) 1998-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/common/wchar.c
 *
 *-------------------------------------------------------------------------
 */
#include "c.h"

#include "mb/pg_wchar.h"
#include "utils/ascii.h"


/*
 * 在今天的除UTF8之外的多字节编码中，这个双字节序列
 * 确保 pg_encoding_mblen() == 2 && pg_encoding_verifymbstr() == 0。
 *
 * 出于历史原因，几个verifychar实现选择特别拒绝
 * 这一对。编码
 * 原作者文档中的字节对范围限制，始终排除了这一对。没有核心转换
 * 能够翻译它。然而，长期以来的verifychar实现
 * 接受任何非NUL字节。big5_to_euc_tw和big5_to_mic甚至翻译
 * 在编码原作者文档中不合法的对。为了避免在安全补丁中收紧
 * 核心或非核心转换，我们寻求这一对。
 *
 * PQescapeString()历史上使用空格作为BYTE1；许多其他值
 * 也可以满足BYTE1的需求。
 */
#define NONUTF8_INVALID_BYTE0 (0x8d)
#define NONUTF8_INVALID_BYTE1 (' ')


/*
 * 多字节编码上的操作由一个辅助
 * 函数表驱动。
 *
 * 要添加编码支持，需要为该编码定义mblen()、dsplen()、verifychar()和
 * verifystr()。对于服务器编码，还需定义mb2wchar()
 * 和wchar2mb()转换函数。
 *
 * 这些函数通常假设它们的输入是有效格式。
 * 文件后面的“验证器”函数需要更加
 * 小心。
 *
 * 我们期望mblen()不需要检查字符的第一个字节以发现正确的长度。
 * GB18030是该规则的一个例外，
 * 因为它还会查看第二个字节。但即便如此，
 * 如果只传递第一个字节，它的行为也是可预测的：它会
 * 将4字节编码的字符视为两个2字节编码的字符，这对于当前所有的使用来说
 * 都足够了。
 *
 * 注意：为了确保psql的显示输出正常工作，dsplen函数的返回值
 * 必须符合Unicode标准。特别是
 * NUL字符是零宽度，控制字符通常
 * 宽度为-1。建议非ASCII编码将其ASCII
 * 子集引用到ASCII例程，以确保一致性。
 */

/*
 * SQL/ASCII
 */
static int fc_pg_ascii2wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		*fc_to++ = *fc_from++;
		fc_len--;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}

static int fc_pg_ascii_mblen(const unsigned char *fc_s)
{
	return 1;
}

static int fc_pg_ascii_dsplen(const unsigned char *fc_s)
{
	if (*fc_s == '\0')
		return 0;
	if (*fc_s < 0x20 || *fc_s == 0x7f)
		return -1;

	return 1;
}

/*
 * EUC
 */
static int fc_pg_euc2wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		if (*fc_from == SS2 && fc_len >= 2)	/* JIS X 0201（所谓的“1字节
										 * KANA”） */
		{
			fc_from++;
			*fc_to = (SS2 << 8) | *fc_from++;
			fc_len -= 2;
		}
		else if (*fc_from == SS3 && fc_len >= 3)	/* JIS X 0212 KANJI */
		{
			fc_from++;
			*fc_to = (SS3 << 16) | (*fc_from++ << 8);
			*fc_to |= *fc_from++;
			fc_len -= 3;
		}
		else if (IS_HIGHBIT_SET(*fc_from) && fc_len >= 2) /* JIS X 0208 KANJI */
		{
			*fc_to = *fc_from++ << 8;
			*fc_to |= *fc_from++;
			fc_len -= 2;
		}
		else					/* 必须是ASCII */
		{
			*fc_to = *fc_from++;
			fc_len--;
		}
		fc_to++;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}

static inline int fc_pg_euc_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (*fc_s == SS2)
		fc_len = 2;
	else if (*fc_s == SS3)
		fc_len = 3;
	else if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;
	else
		fc_len = 1;
	return fc_len;
}

static inline int fc_pg_euc_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (*fc_s == SS2)
		fc_len = 2;
	else if (*fc_s == SS3)
		fc_len = 2;
	else if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);
	return fc_len;
}

/*
 * EUC_JP
 */
static int fc_pg_eucjp2wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	return fc_pg_euc2wchar_with_len(fc_from, fc_to, fc_len);
}

static int fc_pg_eucjp_mblen(const unsigned char *fc_s)
{
	return fc_pg_euc_mblen(fc_s);
}

static int fc_pg_eucjp_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (*fc_s == SS2)
		fc_len = 1;
	else if (*fc_s == SS3)
		fc_len = 2;
	else if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);
	return fc_len;
}

/*
 * EUC_KR
 */
static int fc_pg_euckr2wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	return fc_pg_euc2wchar_with_len(fc_from, fc_to, fc_len);
}

static int fc_pg_euckr_mblen(const unsigned char *fc_s)
{
	return fc_pg_euc_mblen(fc_s);
}

static int fc_pg_euckr_dsplen(const unsigned char *fc_s)
{
	return fc_pg_euc_dsplen(fc_s);
}

/*
 * EUC_CN
 *
 */
static int fc_pg_euccn2wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		if (*fc_from == SS2 && fc_len >= 3)	/* 代码集2（未使用？） */
		{
			fc_from++;
			*fc_to = (SS2 << 16) | (*fc_from++ << 8);
			*fc_to |= *fc_from++;
			fc_len -= 3;
		}
		else if (*fc_from == SS3 && fc_len >= 3)	/* 代码集3（未使用？） */
		{
			fc_from++;
			*fc_to = (SS3 << 16) | (*fc_from++ << 8);
			*fc_to |= *fc_from++;
			fc_len -= 3;
		}
		else if (IS_HIGHBIT_SET(*fc_from) && fc_len >= 2) /* 代码集1 */
		{
			*fc_to = *fc_from++ << 8;
			*fc_to |= *fc_from++;
			fc_len -= 2;
		}
		else
		{
			*fc_to = *fc_from++;
			fc_len--;
		}
		fc_to++;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}

static int fc_pg_euccn_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;
	else
		fc_len = 1;
	return fc_len;
}

static int fc_pg_euccn_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);
	return fc_len;
}

/*
 * EUC_TW
 *
 */
static int fc_pg_euctw2wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		if (*fc_from == SS2 && fc_len >= 4)	/* 代码集2 */
		{
			fc_from++;
			*fc_to = (((uint32) SS2) << 24) | (*fc_from++ << 16);
			*fc_to |= *fc_from++ << 8;
			*fc_to |= *fc_from++;
			fc_len -= 4;
		}
		else if (*fc_from == SS3 && fc_len >= 3)	/* 代码集3（未使用？） */
		{
			fc_from++;
			*fc_to = (SS3 << 16) | (*fc_from++ << 8);
			*fc_to |= *fc_from++;
			fc_len -= 3;
		}
		else if (IS_HIGHBIT_SET(*fc_from) && fc_len >= 2) /* 代码集2 */
		{
			*fc_to = *fc_from++ << 8;
			*fc_to |= *fc_from++;
			fc_len -= 2;
		}
		else
		{
			*fc_to = *fc_from++;
			fc_len--;
		}
		fc_to++;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}

static int fc_pg_euctw_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (*fc_s == SS2)
		fc_len = 4;
	else if (*fc_s == SS3)
		fc_len = 3;
	else if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;
	else
		fc_len = 1;
	return fc_len;
}

static int fc_pg_euctw_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (*fc_s == SS2)
		fc_len = 2;
	else if (*fc_s == SS3)
		fc_len = 2;
	else if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);
	return fc_len;
}

/*
 * 转换pg_wchar到EUC_*编码。
 * 调用者必须为“to”分配足够的空间，包括一个尾随零！
 * len：来自的长度。
 * “from”不一定以null结束。
 */
static int fc_pg_wchar2euc_with_len(const pg_wchar *fc_from, unsigned char *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		unsigned char fc_c;

		if ((fc_c = (*fc_from >> 24)))
		{
			*fc_to++ = fc_c;
			*fc_to++ = (*fc_from >> 16) & 0xff;
			*fc_to++ = (*fc_from >> 8) & 0xff;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 4;
		}
		else if ((fc_c = (*fc_from >> 16)))
		{
			*fc_to++ = fc_c;
			*fc_to++ = (*fc_from >> 8) & 0xff;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 3;
		}
		else if ((fc_c = (*fc_from >> 8)))
		{
			*fc_to++ = fc_c;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 2;
		}
		else
		{
			*fc_to++ = *fc_from;
			fc_cnt++;
		}
		fc_from++;
		fc_len--;
	}
	*fc_to = 0;
	return fc_cnt;
}


/*
 * JOHAB
 */
static int fc_pg_johab_mblen(const unsigned char *fc_s)
{
	return fc_pg_euc_mblen(fc_s);
}

static int fc_pg_johab_dsplen(const unsigned char *fc_s)
{
	return fc_pg_euc_dsplen(fc_s);
}

#ifdef FDD
//将gb18030编码转为wchar编码
static int fc_pg_gb18030_to_wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	int			fc_cnt = 0;
	uint32		fc_c1 = 0,
				fc_c2 = 0,
				fc_c3 = 0,
				fc_c4 = 0;

	while (fc_len > 0 && *fc_from)
	{
		if ((*fc_from & 0x80) == 0)
		{
			*fc_to = *fc_from++;
			fc_len--;
		}
		else if(fc_len >= 2 && *(fc_from+1) >= 0x30 && *(fc_from+1) <= 0x39)
		{
			if (fc_len < 4)
				break;			/* 丢弃尾随不完整字符 */
			fc_c1 = *fc_from++;
			fc_c2 = *fc_from++;
			fc_c3 = *fc_from++;
			fc_c4 = *fc_from++;
			*fc_to = (fc_c1 << 24) | (fc_c2 << 16) | (fc_c3 << 8) | fc_c4;
			fc_len -= 4;
		}
		else
		{
			if (fc_len < 2)
				break;			/* 丢弃尾随不完整字符 */
			fc_c1 = *fc_from++;
			fc_c2 = *fc_from++;
			*fc_to = (fc_c1 << 8) | fc_c2;
			fc_len -= 2;
		}
		fc_to++;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}
#endif //FDD
/*
 * 将UTF8字符串转换为pg_wchar（UCS-4）
 * 调用者必须为“to”分配足够的空间，包括一个尾随零！
 * len：来自的长度。
 * “from”不一定以null结束。
 */
static int fc_pg_utf2wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	int			fc_cnt = 0;
	uint32		fc_c1,
				fc_c2,
				fc_c3,
				fc_c4;

	while (fc_len > 0 && *fc_from)
	{
		if ((*fc_from & 0x80) == 0)
		{
			*fc_to = *fc_from++;
			fc_len--;
		}
		else if ((*fc_from & 0xe0) == 0xc0)
		{
			if (fc_len < 2)
				break;			/* 丢弃尾随不完整字符 */
			fc_c1 = *fc_from++ & 0x1f;
			fc_c2 = *fc_from++ & 0x3f;
			*fc_to = (fc_c1 << 6) | fc_c2;
			fc_len -= 2;
		}
		else if ((*fc_from & 0xf0) == 0xe0)
		{
			if (fc_len < 3)
				break;			/* 丢弃尾随不完整字符 */
			fc_c1 = *fc_from++ & 0x0f;
			fc_c2 = *fc_from++ & 0x3f;
			fc_c3 = *fc_from++ & 0x3f;
			*fc_to = (fc_c1 << 12) | (fc_c2 << 6) | fc_c3;
			fc_len -= 3;
		}
		else if ((*fc_from & 0xf8) == 0xf0)
		{
			if (fc_len < 4)
				break;			/* 丢弃尾随不完整字符 */
			fc_c1 = *fc_from++ & 0x07;
			fc_c2 = *fc_from++ & 0x3f;
			fc_c3 = *fc_from++ & 0x3f;
			fc_c4 = *fc_from++ & 0x3f;
			*fc_to = (fc_c1 << 18) | (fc_c2 << 12) | (fc_c3 << 6) | fc_c4;
			fc_len -= 4;
		}
		else
		{
			/* 将一个伪字符视为长度1；这不是我们抛出错误的责任 */
			*fc_to = *fc_from++;
			fc_len--;
		}
		fc_to++;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}


/*
 * 将一个Unicode码点映射到UTF-8。utf8string必须分配4字节的
 * 空间。
 */
unsigned char * unicode_to_utf8(pg_wchar fc_c, unsigned char *fc_utf8string)
{
	if (fc_c <= 0x7F)
	{
		fc_utf8string[0] = fc_c;
	}
	else if (fc_c <= 0x7FF)
	{
		fc_utf8string[0] = 0xC0 | ((fc_c >> 6) & 0x1F);
		fc_utf8string[1] = 0x80 | (fc_c & 0x3F);
	}
	else if (fc_c <= 0xFFFF)
	{
		fc_utf8string[0] = 0xE0 | ((fc_c >> 12) & 0x0F);
		fc_utf8string[1] = 0x80 | ((fc_c >> 6) & 0x3F);
		fc_utf8string[2] = 0x80 | (fc_c & 0x3F);
	}
	else
	{
		fc_utf8string[0] = 0xF0 | ((fc_c >> 18) & 0x07);
		fc_utf8string[1] = 0x80 | ((fc_c >> 12) & 0x3F);
		fc_utf8string[2] = 0x80 | ((fc_c >> 6) & 0x3F);
		fc_utf8string[3] = 0x80 | (fc_c & 0x3F);
	}

	return fc_utf8string;
}

/*
 * 从pg_wchar到UTF-8的简单转换。
 * 调用者应该为“to”分配足够的空间。
 * len: from的长度。
 * “from”不一定以空字符结束。
 */
static int fc_pg_wchar2utf_with_len(const pg_wchar *fc_from, unsigned char *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		int			fc_char_len;

		unicode_to_utf8(*fc_from, fc_to);
		fc_char_len = pg_utf_mblen(fc_to);
		fc_cnt += fc_char_len;
		fc_to += fc_char_len;
		fc_from++;
		fc_len--;
	}
	*fc_to = 0;
	return fc_cnt;
}

/*
 * 返回指向s的UTF8字符的字节长度
 *
 * 注意：在当前实现中，我们不支持超过4字节的UTF8序列；因此，
 * 不要返回大于4的值。对于任何明显不合法的前导字节或
 * 指示我们支持的长度大于的长度，我们返回“1”。
 *
 * pg_utf2wchar_with_len(), utf8_to_unicode(), pg_utf8_islegal()，以及
 * 其他地方可能需要修改以更改此行为。
 */
int pg_utf_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if ((*fc_s & 0x80) == 0)
		fc_len = 1;
	else if ((*fc_s & 0xe0) == 0xc0)
		fc_len = 2;
	else if ((*fc_s & 0xf0) == 0xe0)
		fc_len = 3;
	else if ((*fc_s & 0xf8) == 0xf0)
		fc_len = 4;
#ifdef NOT_USED
	else if ((*fc_s & 0xfc) == 0xf8)
		fc_len = 5;
	else if ((*fc_s & 0xfe) == 0xfc)
		fc_len = 6;
#endif
	else
		fc_len = 1;
	return fc_len;
}

/*
 * 这是wcwidth()和wcswidth()的实现，如
 * “单一UNIX规范，版本2，开放组，1997年”中定义
 * <http://www.unix.org/online.html>
 *
 * Markus Kuhn -- 2001-09-08 -- 公共领域
 *
 * 为PostgreSQL定制
 *
 * 原版可在此获取： http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
 */

struct mbinterval
{
	unsigned int first;
	unsigned int last;
};

/* 进行区间表的二分查找的辅助函数 */
static int fc_mbbisearch(pg_wchar fc_ucs, const struct mbinterval *fc_table, int fc_max)
{
	int			fc_min = 0;
	int			fc_mid;

	if (fc_ucs < fc_table[0].first || fc_ucs > fc_table[fc_max].last)
		return 0;
	while (fc_max >= fc_min)
	{
		fc_mid = (fc_min + fc_max) / 2;
		if (fc_ucs > fc_table[fc_mid].last)
			fc_min = fc_mid + 1;
		else if (fc_ucs < fc_table[fc_mid].first)
			fc_max = fc_mid - 1;
		else
			return 1;
	}

	return 0;
}


/* 以下函数定义ISO 10646中的字符的列宽如下：
 *
 *	  - 空字符（U+0000）的列宽为0。
 *
 *	  - 其他C0/C1控制字符和DEL将返回
 *		值为-1。
 *
 *	  - 非间距和包围组合字符（Unicode数据库中的
 *		通用类别代码Mn或Me）具有
 *		列宽为0。
 *
 *	  - 在Unicode技术报告#11中定义的东亚宽（W）或东亚
 *		全宽（F）类别中的间距字符的列宽为2。
 *
 *	  - 所有剩余字符（包括所有可打印
 *		ISO 8859-1和WGL4字符、Unicode控制字符等）具有
 *		列宽为1。
 *
 * 此实现假设wchar_t字符编码为ISO 10646。
 */

static int fc_ucs_wcwidth(pg_wchar fc_ucs)
{
#include "common/unicode_combining_table.h"
#include "common/unicode_east_asian_fw_table.h"

	/* 测试8位控制字符 */
	if (fc_ucs == 0)
		return 0;

	if (fc_ucs < 0x20 || (fc_ucs >= 0x7f && fc_ucs < 0xa0) || fc_ucs > 0x0010ffff)
		return -1;

	/*
	 * 在非间距字符表中进行二分查找
	 *
	 * XXX：在官方Unicode源中，可能有一个字符
	 * 被同时描述为非间距和宽字符。截至
	 * Unicode 13.0，将非间距属性视为决定
	 * 显示宽度的因素会导致正确行为，因此请首先进行
	 * 此查找。
	 */
	if (fc_mbbisearch(fc_ucs, combining,
				   sizeof(combining) / sizeof(struct mbinterval) - 1))
		return 0;

	/* 在宽字符表中进行二分查找 */
	if (fc_mbbisearch(fc_ucs, east_asian_fw,
				   sizeof(east_asian_fw) / sizeof(struct mbinterval) - 1))
		return 2;

	return 1;
}

/*
 * 将一个UTF-8字符转换为Unicode码点。
 * 这是pg_utf2wchar_with_len的单字符版本。
 *
 * 此处没有错误检查，c必须指向足够长的字符串。
 */
pg_wchar
utf8_to_unicode(const unsigned char *fc_c)
{
	if ((*fc_c & 0x80) == 0)
		return (pg_wchar) fc_c[0];
	else if ((*fc_c & 0xe0) == 0xc0)
		return (pg_wchar) (((fc_c[0] & 0x1f) << 6) |
						   (fc_c[1] & 0x3f));
	else if ((*fc_c & 0xf0) == 0xe0)
		return (pg_wchar) (((fc_c[0] & 0x0f) << 12) |
						   ((fc_c[1] & 0x3f) << 6) |
						   (fc_c[2] & 0x3f));
	else if ((*fc_c & 0xf8) == 0xf0)
		return (pg_wchar) (((fc_c[0] & 0x07) << 18) |
						   ((fc_c[1] & 0x3f) << 12) |
						   ((fc_c[2] & 0x3f) << 6) |
						   (fc_c[3] & 0x3f));
	else
		/* 这是一个故意无效的代码 */
		return 0xffffffff;
}

static int fc_pg_utf_dsplen(const unsigned char *fc_s)
{
	return fc_ucs_wcwidth(utf8_to_unicode(fc_s));
}

/*
 * 将mule内部代码转换为pg_wchar
 * 调用者应该为“to”分配足够的空间。
 * len: from的长度。
 * “from”不一定以空字符结束。
 */
static int fc_pg_mule2wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		if (IS_LC1(*fc_from) && fc_len >= 2)
		{
			*fc_to = *fc_from++ << 16;
			*fc_to |= *fc_from++;
			fc_len -= 2;
		}
		else if (IS_LCPRV1(*fc_from) && fc_len >= 3)
		{
			fc_from++;
			*fc_to = *fc_from++ << 16;
			*fc_to |= *fc_from++;
			fc_len -= 3;
		}
		else if (IS_LC2(*fc_from) && fc_len >= 3)
		{
			*fc_to = *fc_from++ << 16;
			*fc_to |= *fc_from++ << 8;
			*fc_to |= *fc_from++;
			fc_len -= 3;
		}
		else if (IS_LCPRV2(*fc_from) && fc_len >= 4)
		{
			fc_from++;
			*fc_to = *fc_from++ << 16;
			*fc_to |= *fc_from++ << 8;
			*fc_to |= *fc_from++;
			fc_len -= 4;
		}
		else
		{						/* 假设 ASCII */
			*fc_to = (unsigned char) *fc_from++;
			fc_len--;
		}
		fc_to++;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}

/*
 * 将 pg_wchar 转换为 mule 内部代码
 * 调用者应该为 "to" 分配足够的空间
 * len: from 的长度。
 * "from" 不一定以 null 结尾。
 */
static int fc_pg_wchar2mule_with_len(const pg_wchar *fc_from, unsigned char *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		unsigned char fc_lb;

		fc_lb = (*fc_from >> 16) & 0xff;
		if (IS_LC1(fc_lb))
		{
			*fc_to++ = fc_lb;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 2;
		}
		else if (IS_LC2(fc_lb))
		{
			*fc_to++ = fc_lb;
			*fc_to++ = (*fc_from >> 8) & 0xff;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 3;
		}
		else if (IS_LCPRV1_A_RANGE(fc_lb))
		{
			*fc_to++ = LCPRV1_A;
			*fc_to++ = fc_lb;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 3;
		}
		else if (IS_LCPRV1_B_RANGE(fc_lb))
		{
			*fc_to++ = LCPRV1_B;
			*fc_to++ = fc_lb;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 3;
		}
		else if (IS_LCPRV2_A_RANGE(fc_lb))
		{
			*fc_to++ = LCPRV2_A;
			*fc_to++ = fc_lb;
			*fc_to++ = (*fc_from >> 8) & 0xff;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 4;
		}
		else if (IS_LCPRV2_B_RANGE(fc_lb))
		{
			*fc_to++ = LCPRV2_B;
			*fc_to++ = fc_lb;
			*fc_to++ = (*fc_from >> 8) & 0xff;
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 4;
		}
		else
		{
			*fc_to++ = *fc_from & 0xff;
			fc_cnt += 1;
		}
		fc_from++;
		fc_len--;
	}
	*fc_to = 0;
	return fc_cnt;
}

/* 供 conv.c 直接使用的导出接口 */
int pg_mule_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_LC1(*fc_s))
		fc_len = 2;
	else if (IS_LCPRV1(*fc_s))
		fc_len = 3;
	else if (IS_LC2(*fc_s))
		fc_len = 3;
	else if (IS_LCPRV2(*fc_s))
		fc_len = 4;
	else
		fc_len = 1;				/* 假设 ASCII */
	return fc_len;
}

static int fc_pg_mule_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	/*
	 * 注意：实际上假设所有多字节字符集在屏幕上都是双宽的并不合适。
	 * 但是，这似乎是我们当前支持的 MULE 字符集的一个合理近似。
	 */

	if (IS_LC1(*fc_s))
		fc_len = 1;
	else if (IS_LCPRV1(*fc_s))
		fc_len = 1;
	else if (IS_LC2(*fc_s))
		fc_len = 2;
	else if (IS_LCPRV2(*fc_s))
		fc_len = 2;
	else
		fc_len = 1;				/* 假设 ASCII */

	return fc_len;
}

/*
 * ISO8859-1
 */
static int fc_pg_latin12wchar_with_len(const unsigned char *fc_from, pg_wchar *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		*fc_to++ = *fc_from++;
		fc_len--;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}

/*
 * 从 pg_wchar 到单字节编码的简单转换。只忽略高位。
 * 调用者应该为 "to" 分配足够的空间
 * len: from 的长度。
 * "from" 不一定以 null 结尾。
 */
static int fc_pg_wchar2single_with_len(const pg_wchar *fc_from, unsigned char *fc_to, int fc_len)
{
	int			fc_cnt = 0;

	while (fc_len > 0 && *fc_from)
	{
		*fc_to++ = *fc_from++;
		fc_len--;
		fc_cnt++;
	}
	*fc_to = 0;
	return fc_cnt;
}

static int fc_pg_latin1_mblen(const unsigned char *fc_s)
{
	return 1;
}

static int fc_pg_latin1_dsplen(const unsigned char *fc_s)
{
	return fc_pg_ascii_dsplen(fc_s);
}

/*
 * SJIS
 */
static int fc_pg_sjis_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (*fc_s >= 0xa1 && *fc_s <= 0xdf)
		fc_len = 1;				/* 1 字节的假名？ */
	else if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;				/* 汉字？ */
	else
		fc_len = 1;				/* 应该是 ASCII */
	return fc_len;
}

static int fc_pg_sjis_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (*fc_s >= 0xa1 && *fc_s <= 0xdf)
		fc_len = 1;				/* 1 字节的假名？ */
	else if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;				/* 汉字？ */
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);	/* 应该是 ASCII */
	return fc_len;
}

/*
 * Big5
 */
static int fc_pg_big5_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;				/* 汉字？ */
	else
		fc_len = 1;				/* 应该是 ASCII */
	return fc_len;
}

static int fc_pg_big5_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;				/* 汉字？ */
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);	/* 应该是 ASCII */
	return fc_len;
}

/*
 * GBK
 */
static int fc_pg_gbk_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;				/* 汉字？ */
	else
		fc_len = 1;				/* 应该是 ASCII */
	return fc_len;
}

static int fc_pg_gbk_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;				/* 汉字？ */
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);	/* 应该是 ASCII */
	return fc_len;
}

/*
 * UHC
 */
static int fc_pg_uhc_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;				/* 2 字节？ */
	else
		fc_len = 1;				/* 应该是 ASCII */
	return fc_len;
}

static int fc_pg_uhc_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;				/* 2 字节？ */
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);	/* 应该是 ASCII */
	return fc_len;
}

/*
 * GB18030
 *	由 Bill Huang <bhuang@redhat.com>,<bill_huanghb@ybb.ne.jp> 添加
 */

/*
 * 与所有其他 mblen() 函数不同，此函数还会查看输入的第二个字节。
 * 然而，如果您只传递多字节字符串的第一个字节，第二个字节为 \0，
 * 这仍以可预测的方式工作：
 * 一个 4 字节字符将被报告为两个 2 字节字符。
 * 这对于当前所有用法来说足够了，因为它仅是客户端编码。
 * 之所以这样工作，是因为在任何有效的 4 字节 GB18030 编码字符中，第三和
 * 第四个字节看起来像一个 2 字节编码的字符，当单独观察时。
 */
static int fc_pg_gb18030_mblen(const unsigned char *fc_s)
{
	int			fc_len;

	if (!IS_HIGHBIT_SET(*fc_s))
		fc_len = 1;				/* ASCII */
	else if (*(fc_s + 1) >= 0x30 && *(fc_s + 1) <= 0x39)
		fc_len = 4;
	else
		fc_len = 2;
	return fc_len;
}

static int fc_pg_gb18030_dsplen(const unsigned char *fc_s)
{
	int			fc_len;

	if (IS_HIGHBIT_SET(*fc_s))
		fc_len = 2;
	else
		fc_len = fc_pg_ascii_dsplen(fc_s);	/* ASCII */
	return fc_len;
}

/*
 *-------------------------------------------------------------------
 * 多字节序列验证器
 *
 * verifychar 函数接受 "s"，指向字符串的第一个字节，以及 "len"，字符串的剩余长度。如果从 *s 开始有一个有效编码的字符，则返回其字节长度；否则返回 -1。
 *
 * verifystr 函数也接受 "s"，指向一个字符串和 "len"，字符串的长度。它们验证整个字符串，并返回有效的输入字节数 (<= len)。换句话说，如果整个字符串有效，verifystr 返回 "len"，否则返回第一个无效字符的字节偏移量。verifystr 函数必须测试并拒绝输入中的零字节。
 *
 * verifychar 函数可以假设 len > 0 并且 *s != '\0'，但是它们必须测试并拒绝多字节字符的任何附加字节中的零字节。注意，这一定义允许单字节编码的函数仅为 "return 1"。
 *-------------------------------------------------------------------
 */
static int fc_pg_ascii_verifychar(const unsigned char *fc_s, int fc_len)
{
	return 1;
}

static int fc_pg_ascii_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_nullpos = memchr(fc_s, 0, fc_len);

	if (fc_nullpos == NULL)
		return fc_len;
	else
		return fc_nullpos - fc_s;
}

#define IS_EUC_RANGE_VALID(c)	((c) >= 0xa1 && (c) <= 0xfe)

static int fc_pg_eucjp_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l;
	unsigned char fc_c1,
				fc_c2;

	fc_c1 = *fc_s++;

	switch (fc_c1)
	{
		case SS2:				/* JIS X 0201 */
			fc_l = 2;
			if (fc_l > fc_len)
				return -1;
			fc_c2 = *fc_s++;
			if (fc_c2 < 0xa1 || fc_c2 > 0xdf)
				return -1;
			break;

		case SS3:				/* JIS X 0212 */
			fc_l = 3;
			if (fc_l > fc_len)
				return -1;
			fc_c2 = *fc_s++;
			if (!IS_EUC_RANGE_VALID(fc_c2))
				return -1;
			fc_c2 = *fc_s++;
			if (!IS_EUC_RANGE_VALID(fc_c2))
				return -1;
			break;

		default:
			if (IS_HIGHBIT_SET(fc_c1)) /* JIS X 0208? */
			{
				fc_l = 2;
				if (fc_l > fc_len)
					return -1;
				if (!IS_EUC_RANGE_VALID(fc_c1))
					return -1;
				fc_c2 = *fc_s++;
				if (!IS_EUC_RANGE_VALID(fc_c2))
					return -1;
			}
			else
				/* 必须是ASCII */
			{
				fc_l = 1;
			}
			break;
	}

	return fc_l;
}

static int fc_pg_eucjp_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_eucjp_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_euckr_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l;
	unsigned char fc_c1,
				fc_c2;

	fc_c1 = *fc_s++;

	if (IS_HIGHBIT_SET(fc_c1))
	{
		fc_l = 2;
		if (fc_l > fc_len)
			return -1;
		if (!IS_EUC_RANGE_VALID(fc_c1))
			return -1;
		fc_c2 = *fc_s++;
		if (!IS_EUC_RANGE_VALID(fc_c2))
			return -1;
	}
	else
		/* 必须是ASCII */
	{
		fc_l = 1;
	}

	return fc_l;
}

static int fc_pg_euckr_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_euckr_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

/* EUC-CN 字节序列与 EUC-KR 完全相同 */
#define pg_euccn_verifychar	fc_pg_euckr_verifychar
#define pg_euccn_verifystr	fc_pg_euckr_verifystr

static int fc_pg_euctw_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l;
	unsigned char fc_c1,
				fc_c2;

	fc_c1 = *fc_s++;

	switch (fc_c1)
	{
		case SS2:				/* CNS 11643 平面 1-7 */
			fc_l = 4;
			if (fc_l > fc_len)
				return -1;
			fc_c2 = *fc_s++;
			if (fc_c2 < 0xa1 || fc_c2 > 0xa7)
				return -1;
			fc_c2 = *fc_s++;
			if (!IS_EUC_RANGE_VALID(fc_c2))
				return -1;
			fc_c2 = *fc_s++;
			if (!IS_EUC_RANGE_VALID(fc_c2))
				return -1;
			break;

		case SS3:				/* 未使用 */
			return -1;

		default:
			if (IS_HIGHBIT_SET(fc_c1)) /* CNS 11643 平面 1 */
			{
				fc_l = 2;
				if (fc_l > fc_len)
					return -1;
				/* c1 没有进一步范围检查? */
				fc_c2 = *fc_s++;
				if (!IS_EUC_RANGE_VALID(fc_c2))
					return -1;
			}
			else
				/* 必须是ASCII */
			{
				fc_l = 1;
			}
			break;
	}
	return fc_l;
}

static int fc_pg_euctw_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_euctw_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_johab_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l,
				fc_mbl;
	unsigned char fc_c;

	fc_l = fc_mbl = fc_pg_johab_mblen(fc_s);

	if (fc_len < fc_l)
		return -1;

	if (!IS_HIGHBIT_SET(*fc_s))
		return fc_mbl;

	while (--fc_l > 0)
	{
		fc_c = *++fc_s;
		if (!IS_EUC_RANGE_VALID(fc_c))
			return -1;
	}
	return fc_mbl;
}

static int fc_pg_johab_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_johab_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_mule_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l,
				fc_mbl;
	unsigned char fc_c;

	fc_l = fc_mbl = pg_mule_mblen(fc_s);

	if (fc_len < fc_l)
		return -1;

	while (--fc_l > 0)
	{
		fc_c = *++fc_s;
		if (!IS_HIGHBIT_SET(fc_c))
			return -1;
	}
	return fc_mbl;
}

static int fc_pg_mule_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_mule_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_latin1_verifychar(const unsigned char *fc_s, int fc_len)
{
	return 1;
}

static int fc_pg_latin1_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_nullpos = memchr(fc_s, 0, fc_len);

	if (fc_nullpos == NULL)
		return fc_len;
	else
		return fc_nullpos - fc_s;
}

static int fc_pg_sjis_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l,
				fc_mbl;
	unsigned char fc_c1,
				fc_c2;

	fc_l = fc_mbl = fc_pg_sjis_mblen(fc_s);

	if (fc_len < fc_l)
		return -1;

	if (fc_l == 1)					/* pg_sjis_mblen 已经验证过了 */
		return fc_mbl;

	fc_c1 = *fc_s++;
	fc_c2 = *fc_s;
	if (!ISSJISHEAD(fc_c1) || !ISSJISTAIL(fc_c2))
		return -1;
	return fc_mbl;
}

static int fc_pg_sjis_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_sjis_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_big5_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l,
				fc_mbl;

	fc_l = fc_mbl = fc_pg_big5_mblen(fc_s);

	if (fc_len < fc_l)
		return -1;

	if (fc_l == 2 &&
		fc_s[0] == NONUTF8_INVALID_BYTE0 &&
		fc_s[1] == NONUTF8_INVALID_BYTE1)
		return -1;

	while (--fc_l > 0)
	{
		if (*++fc_s == '\0')
			return -1;
	}

	return fc_mbl;
}

static int fc_pg_big5_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_big5_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_gbk_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l,
				fc_mbl;

	fc_l = fc_mbl = fc_pg_gbk_mblen(fc_s);

	if (fc_len < fc_l)
		return -1;

	if (fc_l == 2 &&
		fc_s[0] == NONUTF8_INVALID_BYTE0 &&
		fc_s[1] == NONUTF8_INVALID_BYTE1)
		return -1;

	while (--fc_l > 0)
	{
		if (*++fc_s == '\0')
			return -1;
	}

	return fc_mbl;
}

static int fc_pg_gbk_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_gbk_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_uhc_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l,
				fc_mbl;

	fc_l = fc_mbl = fc_pg_uhc_mblen(fc_s);

	if (fc_len < fc_l)
		return -1;

	if (fc_l == 2 &&
		fc_s[0] == NONUTF8_INVALID_BYTE0 &&
		fc_s[1] == NONUTF8_INVALID_BYTE1)
		return -1;

	while (--fc_l > 0)
	{
		if (*++fc_s == '\0')
			return -1;
	}

	return fc_mbl;
}

static int fc_pg_uhc_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_uhc_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_gb18030_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l;

	if (!IS_HIGHBIT_SET(*fc_s))
		fc_l = 1;					/* ASCII */
	else if (fc_len >= 4 && *(fc_s + 1) >= 0x30 && *(fc_s + 1) <= 0x39)
	{
		/* 应该是 4 字节，验证剩余字节 */
		if (*fc_s >= 0x81 && *fc_s <= 0xfe &&
			*(fc_s + 2) >= 0x81 && *(fc_s + 2) <= 0xfe &&
			*(fc_s + 3) >= 0x30 && *(fc_s + 3) <= 0x39)
			fc_l = 4;
		else
			fc_l = -1;
	}
	else if (fc_len >= 2 && *fc_s >= 0x81 && *fc_s <= 0xfe)
	{
		/* 应该是 2 字节，验证 */
		if ((*(fc_s + 1) >= 0x40 && *(fc_s + 1) <= 0x7e) ||
			(*(fc_s + 1) >= 0x80 && *(fc_s + 1) <= 0xfe))
			fc_l = 2;
		else
			fc_l = -1;
	}
	else
		fc_l = -1;
	return fc_l;
}

static int fc_pg_gb18030_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_gb18030_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

static int fc_pg_utf8_verifychar(const unsigned char *fc_s, int fc_len)
{
	int			fc_l;

	if ((*fc_s & 0x80) == 0)
	{
		if (*fc_s == '\0')
			return -1;
		return 1;
	}
	else if ((*fc_s & 0xe0) == 0xc0)
		fc_l = 2;
	else if ((*fc_s & 0xf0) == 0xe0)
		fc_l = 3;
	else if ((*fc_s & 0xf8) == 0xf0)
		fc_l = 4;
	else
		fc_l = 1;

	if (fc_l > fc_len)
		return -1;

	if (!pg_utf8_islegal(fc_s, fc_l))
		return -1;

	return fc_l;
}

/*
 * UTF-8 验证器的快速路径使用确定性有限自动机
 * (DFA) 来处理多字节字符。在传统的表驱动 DFA 中，
 * 输入字节和当前状态用于计算状态转换数组的索引。
 * 由于下一个转换的地址依赖于该计算，因此在执行加载指令时存在延迟，
 * 而且 CPU 并未保持忙碌状态。
 *
 * 相反，我们使用如 Per Vognsen 所描述的“基于移位”的 DFA：
 *
 * https://gist.github.com/pervognsen/218ea17743e1442e59bb60d29b1aa725
 *
 * 在基于移位的 DFA 中，输入字节是一个整数数组的索引，
 * 该数组的位模式编码了状态转换。要计算下一个
 * 状态，我们只需将整数右移当前状态并应用掩码。
 * 在这种方案中，转换的地址仅依赖于输入字节，因此
 * 有更好的流水线处理。
 *
 * 状态和转换的命名约定采用自 UTF-8 到 UTF-16/32 的
 * 转码器，其表如下所示：
 *
 * https://github.com/BobSteagall/utf_utils/blob/6b7a465265de2f5fa6133d653df0c9bdd73bbcf8/src/utf_utils.cpp
 *
 * ILL  ASC  CR1  CR2  CR3  L2A  L3A  L3B  L3C  L4A  L4B  L4C CLASS / STATE
 * ==========================================================================
 * err, END, err, err, err, CS1, P3A, CS2, P3B, P4A, CS3, P4B,      | BGN/END
 * err, err, err, err, err, err, err, err, err, err, err, err,      | ERR
 *                                                                  |
 * err, err, END, END, END, err, err, err, err, err, err, err,      | CS1
 * err, err, CS1, CS1, CS1, err, err, err, err, err, err, err,      | CS2
 * err, err, CS2, CS2, CS2, err, err, err, err, err, err, err,      | CS3
 *                                                                  |
 * err, err, err, err, CS1, err, err, err, err, err, err, err,      | P3A
 * err, err, CS1, CS1, err, err, err, err, err, err, err, err,      | P3B
 *                                                                  |
 * err, err, err, CS2, CS2, err, err, err, err, err, err, err,      | P4A
 * err, err, CS2, err, err, err, err, err, err, err, err, err,      | P4B
 *
 * 在最简单的实现中，UTF-8 的基于移位的 DFA
 * 需要 64 位整数来编码转换，但通过 SMT 求解器
 * 找到状态编号使转换能够适配 32 位整数是可能的，正如 Dougall Johnson 所展示的：
 *
 * https://gist.github.com/dougallj/166e326de6ad4cf2c94be97a204c025f
 *
 * 这种打包表示是下面状态值看似奇怪选择的原因。
 */

/* 错误 */
#define	ERR  0
/* 开始 */
#define	BGN 11
/* 继续状态，期望 1/2/3 个继续字节 */
#define	CS1 16
#define	CS2  1
#define	CS3  5
/* 部分状态，其中第一个继续字节的范围受限 */
#define	P3A  6					/* 开头为 E0，检查 3 字节过长 */
#define	P3B 20					/* 开头为 ED，检查代理 */
#define	P4A 25					/* 开头为 F0，检查 4 字节过长 */
#define	P4B 30					/* 主导字节是F4，检查是否过大 */
/* 开始和结束是相同状态 */
#define	END BGN

/* 查找表的编码状态转移 */

/* ASCII */
#define ASC (END << BGN)
/* 2字节主导字节 */
#define L2A (CS1 << BGN)
/* 3字节主导字节 */
#define L3A (P3A << BGN)
#define L3B (CS2 << BGN)
#define L3C (P3B << BGN)
/* 4字节主导字节 */
#define L4A (P4A << BGN)
#define L4B (CS3 << BGN)
#define L4C (P4B << BGN)
/* 延续字节 */
#define CR1 (END << CS1) | (CS1 << CS2) | (CS2 << CS3) | (CS1 << P3B) | (CS2 << P4B)
#define CR2 (END << CS1) | (CS1 << CS2) | (CS2 << CS3) | (CS1 << P3B) | (CS2 << P4A)
#define CR3 (END << CS1) | (CS1 << CS2) | (CS2 << CS3) | (CS1 << P3A) | (CS2 << P4A)
/* 无效字节 */
#define ILL ERR

static const uint32 Utf8Transition[256] =
{
	/* ASCII */

	ILL, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,

	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,

	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,

	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
	ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,

	/* 延续字节 */

	/* 80..8F */
	CR1, CR1, CR1, CR1, CR1, CR1, CR1, CR1,
	CR1, CR1, CR1, CR1, CR1, CR1, CR1, CR1,

	/* 90..9F */
	CR2, CR2, CR2, CR2, CR2, CR2, CR2, CR2,
	CR2, CR2, CR2, CR2, CR2, CR2, CR2, CR2,

	/* A0..BF */
	CR3, CR3, CR3, CR3, CR3, CR3, CR3, CR3,
	CR3, CR3, CR3, CR3, CR3, CR3, CR3, CR3,
	CR3, CR3, CR3, CR3, CR3, CR3, CR3, CR3,
	CR3, CR3, CR3, CR3, CR3, CR3, CR3, CR3,

	/* 主导字节 */

	/* C0..DF */
	ILL, ILL, L2A, L2A, L2A, L2A, L2A, L2A,
	L2A, L2A, L2A, L2A, L2A, L2A, L2A, L2A,
	L2A, L2A, L2A, L2A, L2A, L2A, L2A, L2A,
	L2A, L2A, L2A, L2A, L2A, L2A, L2A, L2A,

	/* E0..EF */
	L3A, L3B, L3B, L3B, L3B, L3B, L3B, L3B,
	L3B, L3B, L3B, L3B, L3B, L3C, L3B, L3B,

	/* F0..FF */
	L4A, L4B, L4B, L4B, L4C, ILL, ILL, ILL,
	ILL, ILL, ILL, ILL, ILL, ILL, ILL, ILL
};

static void fc_utf8_advance(const unsigned char *fc_s, uint32 *fc_state, int fc_len)
{
	/* 注意：我们故意不检查此处的状态值。 */
	while (fc_len > 0)
	{
		/*
		 * 掩码值为31是重要的：在大多数指令集中的
		 * 32位操作数的位移理解为其模32的位移，
		 * 所以编译器应该省略掩码操作。
		 */
		*fc_state = Utf8Transition[*fc_s++] >> (*fc_state & 31);
		fc_len--;
	}

	*fc_state &= 31;
}

static int fc_pg_utf8_verifystr(const unsigned char *fc_s, int fc_len)
{
	const unsigned char *fc_start = fc_s;
	const int	fc_orig_len = fc_len;
	uint32		fc_state = BGN;

/*
 * 十六似乎在不同字节分布中提供了最佳性能平衡。
 */
#define STRIDE_LENGTH 16

	if (fc_len >= STRIDE_LENGTH)
	{
		while (fc_len >= STRIDE_LENGTH)
		{
			/*
			 * 如果块都是ASCII，我们可以跳过完整的UTF-8检查，
			 * 但我们必须首先检查非-END状态，这意味着
			 * 先前的块在多字节序列的中间结束。
			 */
			if (fc_state != END || !is_valid_ascii(fc_s, STRIDE_LENGTH))
				fc_utf8_advance(fc_s, &fc_state, STRIDE_LENGTH);

			fc_s += STRIDE_LENGTH;
			fc_len -= STRIDE_LENGTH;
		}

		/* 错误状态持续存在，因此我们只需在这里检查它。 */
		if (fc_state == ERR)
		{
			/*
			 * 从头开始使用慢路径，以便我们可以
			 * 计算有效字节。
			 */
			fc_len = fc_orig_len;
			fc_s = fc_start;
		}
		else if (fc_state != END)
		{
			/*
			 * 快速路径在多字节序列中途退出。
			 * 向后查找以找到主导字节，以便慢路径
			 * 可以从那里恢复检查。我们必须始终向后回溯
			 * 至少一个字节，因为当前字节可能是，例如，在2字节主导字节后的ASCII字节，这是无效的。
			 */
			do
			{
				Assert(fc_s > fc_start);
				fc_s--;
				fc_len++;
				Assert(IS_HIGHBIT_SET(*fc_s));
			} while (pg_utf_mblen(fc_s) <= 1);
		}
	}

	/* 检查剩余字节 */
	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_s))
		{
			if (*fc_s == '\0')
				break;
			fc_l = 1;
		}
		else
		{
			fc_l = fc_pg_utf8_verifychar(fc_s, fc_len);
			if (fc_l == -1)
				break;
		}
		fc_s += fc_l;
		fc_len -= fc_l;
	}

	return fc_s - fc_start;
}

/*
 * 检查单个UTF-8编码字符的有效性
 *
 * 这直接实现了RFC3629中的规则。第二个字节上看似奇怪的
 * 限制旨在确保不存在一个给定Unicode字符点的多个编码；
 * 也就是说，你不能使用多于必要的高次零位字节序列
 * 来表示一个可以用更少字节表示的字符。
 * 否则会产生安全隐患（例如，创建一个表面上看似
 * 非ASCII字符但解码为普通ASCII的字符）。
 *
 * 长度假定由pg_utf_mblen()获得，
 * 调用者必须检查缓冲区中是否存在那么多字节。
 */
bool pg_utf8_islegal(const unsigned char *fc_source, int fc_length)
{
	unsigned char fc_a;

	switch (fc_length)
	{
		default:
			/* 当前拒绝长度为5和6 */
			return false;
		case 4:
			fc_a = fc_source[3];
			if (fc_a < 0x80 || fc_a > 0xBF)
				return false;
			/* FALL THRU */
		case 3:
			fc_a = fc_source[2];
			if (fc_a < 0x80 || fc_a > 0xBF)
				return false;
			/* FALL THRU */
		case 2:
			fc_a = fc_source[1];
			switch (*fc_source)
			{
				case 0xE0:
					if (fc_a < 0xA0 || fc_a > 0xBF)
						return false;
					break;
				case 0xED:
					if (fc_a < 0x80 || fc_a > 0x9F)
						return false;
					break;
				case 0xF0:
					if (fc_a < 0x90 || fc_a > 0xBF)
						return false;
					break;
				case 0xF4:
					if (fc_a < 0x80 || fc_a > 0x8F)
						return false;
					break;
				default:
					if (fc_a < 0x80 || fc_a > 0xBF)
						return false;
					break;
			}
			/* FALL THRU */
		case 1:
			fc_a = *fc_source;
			if (fc_a >= 0x80 && fc_a < 0xC2)
				return false;
			if (fc_a > 0xF4)
				return false;
			break;
	}
	return true;
}


/*
 * 用两个字节填充提供的缓冲区，使得：
 *   pg_encoding_mblen(dst) == 2 && pg_encoding_verifymbstr(dst) == 0
 */
void pg_encoding_set_invalid(int fc_encoding, char *fc_dst)
{
	Assert(pg_encoding_max_length(fc_encoding) > 1);

	fc_dst[0] = (fc_encoding == PG_UTF8 ? 0xc0 : NONUTF8_INVALID_BYTE0);
	fc_dst[1] = NONUTF8_INVALID_BYTE1;
}

/*
 *-------------------------------------------------------------------
 * 编码信息表
 * XXX 必须按照mb/pg_wchar.h中枚举pg_enc的相同顺序排序
 *-------------------------------------------------------------------
 */
const pg_wchar_tbl pg_wchar_table[] = {
	{fc_pg_ascii2wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_ascii_mblen, fc_pg_ascii_dsplen, fc_pg_ascii_verifychar, fc_pg_ascii_verifystr, 1},	/* PG_SQL_ASCII */
	{fc_pg_eucjp2wchar_with_len, fc_pg_wchar2euc_with_len, fc_pg_eucjp_mblen, fc_pg_eucjp_dsplen, fc_pg_eucjp_verifychar, fc_pg_eucjp_verifystr, 3},	/* PG_EUC_JP */
	{fc_pg_euccn2wchar_with_len, fc_pg_wchar2euc_with_len, fc_pg_euccn_mblen, fc_pg_euccn_dsplen, pg_euccn_verifychar, pg_euccn_verifystr, 2},	/* PG_EUC_CN */
	{fc_pg_euckr2wchar_with_len, fc_pg_wchar2euc_with_len, fc_pg_euckr_mblen, fc_pg_euckr_dsplen, fc_pg_euckr_verifychar, fc_pg_euckr_verifystr, 3},	/* PG_EUC_KR */
	{fc_pg_euctw2wchar_with_len, fc_pg_wchar2euc_with_len, fc_pg_euctw_mblen, fc_pg_euctw_dsplen, fc_pg_euctw_verifychar, fc_pg_euctw_verifystr, 4},	/* PG_EUC_TW */
	{fc_pg_eucjp2wchar_with_len, fc_pg_wchar2euc_with_len, fc_pg_eucjp_mblen, fc_pg_eucjp_dsplen, fc_pg_eucjp_verifychar, fc_pg_eucjp_verifystr, 3},	/* PG_EUC_JIS_2004 */
	{fc_pg_utf2wchar_with_len, fc_pg_wchar2utf_with_len, pg_utf_mblen, fc_pg_utf_dsplen, fc_pg_utf8_verifychar, fc_pg_utf8_verifystr, 4},	/* PG_UTF8 */
	{fc_pg_mule2wchar_with_len, fc_pg_wchar2mule_with_len, pg_mule_mblen, fc_pg_mule_dsplen, fc_pg_mule_verifychar, fc_pg_mule_verifystr, 4},	/* PG_MULE_INTERNAL */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN1 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN2 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN3 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN4 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN5 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN6 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN7 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN8 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN9 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_LATIN10 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1256 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1258 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN866 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN874 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_KOI8R */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1251 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1252 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* ISO-8859-5 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* ISO-8859-6 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* ISO-8859-7 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* ISO-8859-8 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1250 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1253 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1254 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1255 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_WIN1257 */
	{fc_pg_latin12wchar_with_len, fc_pg_wchar2single_with_len, fc_pg_latin1_mblen, fc_pg_latin1_dsplen, fc_pg_latin1_verifychar, fc_pg_latin1_verifystr, 1},	/* PG_KOI8U */
	{0, 0, fc_pg_sjis_mblen, fc_pg_sjis_dsplen, fc_pg_sjis_verifychar, fc_pg_sjis_verifystr, 2},	/* PG_SJIS */
	{0, 0, fc_pg_big5_mblen, fc_pg_big5_dsplen, fc_pg_big5_verifychar, fc_pg_big5_verifystr, 2},	/* PG_BIG5 */
	{0, 0, fc_pg_gbk_mblen, fc_pg_gbk_dsplen, fc_pg_gbk_verifychar, fc_pg_gbk_verifystr, 2},	/* PG_GBK */
	{0, 0, fc_pg_uhc_mblen, fc_pg_uhc_dsplen, fc_pg_uhc_verifychar, fc_pg_uhc_verifystr, 2},	/* PG_UHC */
#ifndef FDD
	{0, 0, pg_gb18030_mblen, pg_gb18030_dsplen, pg_gb18030_verifychar, pg_gb18030_verifystr, 4},	/* PG_GB18030 */
#else
	{fc_pg_gb18030_to_wchar_with_len, fc_pg_wchar2euc_with_len, fc_pg_gb18030_mblen, fc_pg_gb18030_dsplen, fc_pg_gb18030_verifychar, fc_pg_gb18030_verifystr, 4},	/* PG_GB18030 */
#endif //FDD
	{0, 0, fc_pg_johab_mblen, fc_pg_johab_dsplen, fc_pg_johab_verifychar, fc_pg_johab_verifystr, 3},	/* PG_JOHAB */
	{0, 0, fc_pg_sjis_mblen, fc_pg_sjis_dsplen, fc_pg_sjis_verifychar, fc_pg_sjis_verifystr, 2} /* PG_SHIFT_JIS_2004 */
};

/*
 * 返回多字节字符的字节长度。
 *
 * 注意：处理不确定在指定编码中有效的文本时，结果可能超过实际剩余
 * 字符串长度。调用者如果不能处理此情况，应使用 pg_encoding_mblen_bounded()。
 */
int pg_encoding_mblen(int fc_encoding, const char *fc_mbstr)
{
	return (PG_VALID_ENCODING(fc_encoding) ?
			pg_wchar_table[fc_encoding].mblen((const unsigned char *) fc_mbstr) :
			pg_wchar_table[PG_SQL_ASCII].mblen((const unsigned char *) fc_mbstr));
}

/*
 * 返回多字节字符的字节长度，但不超过
 * 字符串末尾的距离。
 */
int pg_encoding_mblen_bounded(int fc_encoding, const char *fc_mbstr)
{
	return strnlen(fc_mbstr, pg_encoding_mblen(fc_encoding, fc_mbstr));
}

/*
 * 返回多字节字符的显示长度。
 */
int pg_encoding_dsplen(int fc_encoding, const char *fc_mbstr)
{
	return (PG_VALID_ENCODING(fc_encoding) ?
			pg_wchar_table[fc_encoding].dsplen((const unsigned char *) fc_mbstr) :
			pg_wchar_table[PG_SQL_ASCII].dsplen((const unsigned char *) fc_mbstr));
}

/*
 * 验证给定字符串的第一个多字节字符。
 * 如果有效，则返回其字节长度，如果无效，则返回 -1。 （有关
 * mbverifychar API 的完整详细信息，请参见上面的注释。）
 */
int pg_encoding_verifymbchar(int fc_encoding, const char *fc_mbstr, int fc_len)
{
	return (PG_VALID_ENCODING(fc_encoding) ?
			pg_wchar_table[fc_encoding].mbverifychar((const unsigned char *) fc_mbstr, fc_len) :
			pg_wchar_table[PG_SQL_ASCII].mbverifychar((const unsigned char *) fc_mbstr, fc_len));
}

/*
 * 验证字符串是否适用于给定编码。
 * 返回形成有效字符串的输入字节数（<= len）。
 * （有关 mbverifystr API 的完整详细信息，请参见上面的注释。）
 */
int pg_encoding_verifymbstr(int fc_encoding, const char *fc_mbstr, int fc_len)
{
	return (PG_VALID_ENCODING(fc_encoding) ?
			pg_wchar_table[fc_encoding].mbverifystr((const unsigned char *) fc_mbstr, fc_len) :
			pg_wchar_table[PG_SQL_ASCII].mbverifystr((const unsigned char *) fc_mbstr, fc_len));
}

/*
 * 获取给定编码的最大长度
 */
int pg_encoding_max_length(int fc_encoding)
{
	Assert(PG_VALID_ENCODING(fc_encoding));

	/*
	 * 尽管有 assert 进行编码检查，但由于某些 mingw 版本的原因
	 * 否则会发出虚假警告。
	 */
	return PG_VALID_ENCODING(fc_encoding) ?
		pg_wchar_table[fc_encoding].maxmblen :
		pg_wchar_table[PG_SQL_ASCII].maxmblen;
}
